package data;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;

import javax.xml.stream.XMLEventFactory;
import javax.xml.stream.XMLEventReader;
import javax.xml.stream.XMLEventWriter;
import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLOutputFactory;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.events.Characters;
import javax.xml.stream.events.EndElement;
import javax.xml.stream.events.StartDocument;
import javax.xml.stream.events.StartElement;
import javax.xml.stream.events.XMLEvent;

import model.Activity;
import model.Food;
import model.DailyLog;

/**
 * 
 * Reads/Writes to the Daily Log database. 
 * @author Roman Pusec, Dennis Goldfinger, Marko Bilic
 *
 */
public class DailyLogDao implements IDao<DailyLog> {

	private String filepath;
	static final String ENTRY = "entry";
	static final String DATE = "date";
	static final String DESIRED_WEIGHT = "desiredWeight";
	static final String CALORIE_LIMIT = "calorieLimit";
	static final String INTAKE = "intake";
	static final String FOOD = "food";
	static final String FOOD_QUANTITY = "foodQuantity";
	static final String FOOD_NAME = "foodName";
	static final String ACTIVITY_QUANTITY = "activityQuantity";
	static final String ACTIVITY_NAME = "activityName";
	static final String ACTIVITY = "activity";
	static final String EXERCISES = "exercises";
	static final String LOG = "log";
	
	/**
	 * Constructor which specifies the filepath. 
	 * @param filepath The path to the database. 
	 */
	public DailyLogDao(String filepath)
	{
		this.filepath = filepath;
	}
    
	/**
	 * Gets all of the DailyLogs from the database. 
	 */
	@Override
	public ArrayList<DailyLog> getAll() {

	    ArrayList<DailyLog> items = new ArrayList<DailyLog>();
	    try {
	    	// First, create a new XMLInputFactory
	        XMLInputFactory inputFactory = XMLInputFactory.newInstance();
	        // Setup a new eventReader
	        InputStream in = new FileInputStream(filepath);
	        XMLEventReader eventReader = inputFactory.createXMLEventReader(in);
	        // read the XML document
	        DailyLog dailyLog = null;
	        Food tempFood = null;
	        Activity tempActivity = null;
	        int tempQuantity = 0;

	        while (eventReader.hasNext()) 
	        {
	        	XMLEvent event = eventReader.nextEvent();

		        if (event.isStartElement()) 
		        {
		        	StartElement startElement = event.asStartElement();
		            // If we have an item element, we create a new item
		            if (startElement.getName().getLocalPart() == (ENTRY)) 
		            {
		        	    dailyLog = new DailyLog(0, 0, "");
		            }
	   
					if (event.asStartElement().getName().getLocalPart().equals(DATE)) 
					{
					    event = eventReader.nextEvent();
					    dailyLog.setDate(event.asCharacters().getData());
					    continue;
					}
		          
		            if (event.asStartElement().getName().getLocalPart().equals(DESIRED_WEIGHT)) 
		            {
		                event = eventReader.nextEvent();
		                dailyLog.setWeight(Double.parseDouble(event.asCharacters().getData()));
		                continue;
		            }
		            
		            if (event.asStartElement().getName().getLocalPart().equals(CALORIE_LIMIT)) 
		            {
		                event = eventReader.nextEvent();
		                dailyLog.setLimit(Double.parseDouble(event.asCharacters().getData()));
		                continue;
		            }
		            
		            if (event.asStartElement().getName().getLocalPart().equals(FOOD_NAME)) 
		            {
		                event = eventReader.nextEvent();
		                tempFood = new Food(event.asCharacters().getData());
		                continue;
		            }
		          
		            if (event.asStartElement().getName().getLocalPart().equals(FOOD_QUANTITY)) 
		            {
			            event = eventReader.nextEvent();
			            tempQuantity = Integer.parseInt(event.asCharacters().getData());
			            
			            if(tempFood != null && tempQuantity > 0)
			            {
			            	dailyLog.addToIntake(tempFood, tempQuantity);
			            	tempFood = null;
			            	tempQuantity = 0;
			            }
			            
			            continue;
		            }
		            
		            if (event.asStartElement().getName().getLocalPart().equals(ACTIVITY_NAME)) 
		            {
		                event = eventReader.nextEvent();
		                tempActivity = new Activity(event.asCharacters().getData());
		                continue;
		            }
		          
		            if (event.asStartElement().getName().getLocalPart().equals(ACTIVITY_QUANTITY)) 
		            {
			            event = eventReader.nextEvent();
			            tempQuantity = Integer.parseInt(event.asCharacters().getData());
			            
			            if(tempActivity != null && tempQuantity > 0)
			            {
			            	dailyLog.addToActivities(tempActivity, tempQuantity);
			            	tempActivity = null;
			            	tempQuantity = 0;
			            }
			            
			            continue;
		            }
		        }
		        // If we reach the end of an item element, we add it to the list
		        if (event.isEndElement()) 
		        {
		            EndElement endElement = event.asEndElement();
		            if (endElement.getName().getLocalPart() == (ENTRY)) 
		            {
		            	items.add(dailyLog);
		            }
		        }
	        
		    }
	    } catch (FileNotFoundException e) {
	      e.printStackTrace();
	    } catch (XMLStreamException e) {
	      e.printStackTrace();
	    }
	    return items;
	}
	
	/**
	 * Writes all of the DailyLogs to the database. 
	 */
	@Override
	public void writeAll(ArrayList<DailyLog> DailyLogArr) throws Exception {
		
		// create an XMLOutputFactory
	    XMLOutputFactory outputFactory = XMLOutputFactory.newInstance();
	    // create XMLEventWriter
	    XMLEventWriter eventWriter = outputFactory.createXMLEventWriter(new FileOutputStream(filepath));
	    // create an EventFactory
	    XMLEventFactory eventFactory = XMLEventFactory.newInstance();
	    XMLEvent end = eventFactory.createDTD("\n");
	    XMLEvent tab = eventFactory.createDTD("\t");
	    // create and write Start Tag
	    StartDocument startDocument = eventFactory.createStartDocument();
	    eventWriter.add(startDocument);

	    // create config open tag
	    eventWriter.add(end);
	    StartElement itemsStartElement = eventFactory.createStartElement("", "", LOG);
	    eventWriter.add(itemsStartElement);
	    eventWriter.add(end);
	    
	    for(DailyLog dailyLog : DailyLogArr)
	    {
	    	StartElement singleItemStartElement = eventFactory.createStartElement("", "", "entry");
	    	eventWriter.add(tab);
	    	eventWriter.add(singleItemStartElement);
	    	eventWriter.add(end);
	    	
	    	// Write the different nodes
		    createNode(eventWriter, DATE, dailyLog.getDate(), eventFactory.createDTD("\t\t"));
		    createNode(eventWriter, DESIRED_WEIGHT, Double.toString(dailyLog.getWeight()), eventFactory.createDTD("\t\t"));
		    createNode(eventWriter, CALORIE_LIMIT, Double.toString(dailyLog.getLimit()), eventFactory.createDTD("\t\t"));
		    
		    eventWriter.add(tab);
		    eventWriter.add(tab);
		    eventWriter.add(eventFactory.createStartElement("", "", INTAKE));
		    eventWriter.add(end);
		    
		    // -- Food --
		    HashMap<Food, Integer> foodMap = dailyLog.getIntake();
		    Object[] foodKeys = foodMap.keySet().toArray();
			Object[] foodValues = foodMap.values().toArray();
			
			for(int i = 0; i < foodMap.size(); i++)
			{
				eventWriter.add(tab);
				eventWriter.add(tab);
				eventWriter.add(tab);
				eventWriter.add(eventFactory.createStartElement("", "", FOOD));
				eventWriter.add(end);
				createNode(eventWriter, FOOD_NAME, ((Food)foodKeys[i]).getName(), eventFactory.createDTD("\t\t\t\t"));
				createNode(eventWriter, FOOD_QUANTITY, foodValues[i].toString(), eventFactory.createDTD("\t\t\t\t"));
				eventWriter.add(tab);
				eventWriter.add(tab);
				eventWriter.add(tab);
				eventWriter.add(eventFactory.createEndElement("", "", FOOD));
				eventWriter.add(end);
			}
			
			eventWriter.add(tab);
			eventWriter.add(tab);
		    eventWriter.add(eventFactory.createEndElement("", "", INTAKE));
		    eventWriter.add(end);
		    
		    eventWriter.add(tab);
		    eventWriter.add(tab);
		    eventWriter.add(eventFactory.createStartElement("", "", EXERCISES));
		    eventWriter.add(end);
		    
		    // -- Activity --
		    HashMap<Activity, Integer> activityMap = dailyLog.getActivities();
		    Object[] activityKeys = activityMap.keySet().toArray();
			Object[] activityValues = activityMap.values().toArray();
			
			for(int i = 0; i < activityMap.size(); i++)
			{
				eventWriter.add(tab);
				eventWriter.add(tab);
				eventWriter.add(tab);
				eventWriter.add(eventFactory.createStartElement("", "", ACTIVITY));
				eventWriter.add(end);
				createNode(eventWriter, ACTIVITY_NAME, ((Activity)activityKeys[i]).getName(), eventFactory.createDTD("\t\t\t\t"));
				createNode(eventWriter, ACTIVITY_QUANTITY, activityValues[i].toString(), eventFactory.createDTD("\t\t\t\t"));
				eventWriter.add(tab);
				eventWriter.add(tab);
				eventWriter.add(tab);
				eventWriter.add(eventFactory.createEndElement("", "", ACTIVITY));
				eventWriter.add(end);
			}
		    
			eventWriter.add(tab);
			eventWriter.add(tab);
		    eventWriter.add(eventFactory.createEndElement("", "", EXERCISES));
		    eventWriter.add(end);
			
		    eventWriter.add(tab);
		    eventWriter.add(eventFactory.createEndElement("", "", "entry"));
		    eventWriter.add(end);
	    }
	    
	    eventWriter.add(eventFactory.createEndElement("", "", LOG));
	    eventWriter.add(end);
	    eventWriter.add(eventFactory.createEndDocument());
	    eventWriter.close();
		
	}
	
	/**
	 * Creates a node in the XML file. 
	 * @param eventWriter The event writer. 
	 * @param name The name of the node. 
	 * @param value The value of the node. 
	 * @throws XMLStreamException 
	 */
	private void createNode(XMLEventWriter eventWriter, String name, String value, XMLEvent tab) throws XMLStreamException 
	{	
	    XMLEventFactory eventFactory = XMLEventFactory.newInstance();
	    XMLEvent end = eventFactory.createDTD("\n");
	    // create Start node
	    StartElement sElement = eventFactory.createStartElement("", "", name);
	    eventWriter.add(tab);
	    eventWriter.add(sElement);
	    // create Content
	    Characters characters = eventFactory.createCharacters(value);
	    eventWriter.add(characters);
	    // create End node
	    EndElement eElement = eventFactory.createEndElement("", "", name);
	    eventWriter.add(eElement);
	    eventWriter.add(end);	
	}

}
